// Copyright (c) Appeon
// The following has been modified by Appeon under compliance with the Apache 2.0 license from the original work

// Copyright(c) 2002-2019, Npgsql
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written agreement
// is hereby granted, provided that the above copyright notice and this
// paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL NPGSQL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// Npgsql HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// NPGSQL SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE.THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND Npgsql
// HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
// OR MODIFICATIONS.

using System;
using JetBrains.Annotations;
using SnapObjects.Commons;
using NpgsqlTypes;
using System.Text.Json;
using System.IO;
using System.Text;

namespace SnapObjects.Data.PostgreSql
{
    public class PostgreSqlJsonTypeMapping : PostgreSqlTypeMapping
    {
        public PostgreSqlJsonTypeMapping(
            [NotNull] string storeType, NpgsqlDbType npgsqlDbType, Type clrType)
            : base(storeType, clrType, npgsqlDbType)
        {
        }

        protected virtual string EscapeSqlLiteral([NotNull] string literal)
            => Check.NotNull(literal, nameof(literal)).Replace("'", "''");

        protected override string GenerateNonNullSqlLiteral(object value)
        {
            switch (value)
            {
                case JsonDocument _:
                case JsonElement _:
                {
                    using (var stream = new MemoryStream())
                    {
                        using (var writer = new Utf8JsonWriter(stream))
                        {
                            if (value is JsonDocument d)
                            {
                                d.WriteTo(writer);
                            }
                            else
                            {
                                ((JsonElement)value).WriteTo(writer);
                            }

                            writer.Flush();

                            return $"'{this.EscapeSqlLiteral(Encoding.UTF8.GetString(stream.ToArray()))}'";
                        }
                    }
                }
                case string s:
                    return $"'{this.EscapeSqlLiteral(s)}'";
                default:
                    return $"'{this.EscapeSqlLiteral(JsonSerializer.Serialize(value))}'";
            }
        }

        public override TypeMapping Clone([NotNull] string dbTypeName, int? size)
        {
            return new PostgreSqlJsonTypeMapping(dbTypeName, this.NpgsqlDbType, this.ClrType);
        }
    }
}
